﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using OSGeo.MapGuide;
using OSGeo.MapGuide.Viewer;
using OSGeo.MapGuide.Viewer.Desktop;

namespace MapViewerTest
{
    public partial class Form1 : Form, IMapStatusBar
    {
        private MgServiceFactory fact;

        public Form1()
        {
            InitializeComponent();
        }

        protected override void OnLoad(EventArgs e)
        {
            new MapViewerController(viewer, legend, this, propertyPane, toolbar);
            _agfRW = new MgAgfReaderWriter();
            _wktRW = new MgWktReaderWriter();
            _geomFact = new MgGeometryFactory();
            fact = new MgServiceFactory();
            base.OnLoad(e);
        }

        public void SetCursorPositionMessage(string message)
        {
            lblCoords.Text = message;
        }

        public void SetFeatureSelectedMessage(string message)
        {
            lblSelection.Text = message;
        }

        public void SetMapScaleMessage(string message)
        {
            lblScale.Text = message;
        }

        public void SetMapSizeMessage(string message)
        {
            lblSize.Text = message;
        }

        private void btnRefresh_Click(object sender, EventArgs e)
        {
            viewer.RefreshMap();
        }

        private void btnZoomExtents_Click(object sender, EventArgs e)
        {
            viewer.InitialMapView();   
        }

        private void btnZoomOut_Click(object sender, EventArgs e)
        {
            viewer.ActiveTool = MapActiveTool.ZoomOut;   
        }

        private void openMapToolStripMenuItem_Click(object sender, EventArgs e)
        {
            using (var diag = new ResourceIdDialog())
            {
                if (diag.ShowDialog() == DialogResult.OK)
                {
                    LoadMap(diag.ResourceID);
                }
            }
        }

        private MgWktReaderWriter _wktRW;
        private MgAgfReaderWriter _agfRW;
        private MgGeometryFactory _geomFact;
        private MgdLayer _redlineLayer;
        private MgdMap _map;

        public void LoadMap(MgResourceIdentifier resourceId)
        {
            //viewer.ConvertTiledGroupsToNonTiled = true;
            _map = new MgdMap(resourceId);
            var fact = new MgServiceFactory();

            viewer.Init(
                new MgDesktopMapViewerProvider(
                    _map, 
                    (MgdResourceService)fact.CreateService(MgServiceType.ResourceService), 
                    (MgRenderingService)fact.CreateService(MgServiceType.RenderingService)));
            UpdateButtonCheckedState();

            /*
            //This code fragment is to prove that the viewer will
            //start with a rendered selection even if a rendering 
            //operation is in progress
            var query = new MgFeatureQueryOptions();
            query.SetFilter("Autogenerated_SDF_ID = 2");
            var layers = _map.GetLayers();
            var layer = layers.GetItem("Districts");

            var reader = layer.SelectFeatures(query);
            var selection = viewer.GetSelection();
            selection.AddFeatures(layer, reader, 0);
            reader.Close();

            viewer.UpdateSelection();
            */
        }

        private void quitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void btnPan_Click(object sender, EventArgs e)
        {
            viewer.ActiveTool = MapActiveTool.Pan;   
        }

        private void btnSelect_Click(object sender, EventArgs e)
        {
            viewer.ActiveTool = MapActiveTool.Select;
        }

        private void btnZoom_Click(object sender, EventArgs e)
        {
            viewer.ActiveTool = MapActiveTool.ZoomIn;
        }

        private void btnClearSelection_Click(object sender, EventArgs e)
        {
            viewer.ClearSelection();
        }

        private void loadPackageToolStripMenuItem_Click(object sender, EventArgs e)
        {
            using (var open = new OpenFileDialog())
            {
                open.Filter = "MapGuide Packages (*.mgp)|*.mgp";
                if (open.ShowDialog() == DialogResult.OK)
                {
                    var fact = new MgServiceFactory();
                    var resSvc = (MgResourceService)fact.CreateService(MgServiceType.ResourceService);
                    var source = new MgByteSource(open.FileName);
                    resSvc.ApplyResourcePackage(source.GetReader());
                    MessageBox.Show("Package Loaded");
                }
            }
        }

        private void CheckRedlineLayer()
        {
            if (_redlineLayer == null)
            {
                var fact = new MgServiceFactory();
                var featSvc = (MgdFeatureService)fact.CreateService(MgServiceType.FeatureService);
                var resSvc = (MgResourceService)fact.CreateService(MgServiceType.ResourceService);

                var sessionId = Guid.NewGuid().ToString();
                var fsId = new MgResourceIdentifier("Session:" + sessionId + "//Redline.FeatureSource");
                var ldfId = new MgResourceIdentifier("Session:" + sessionId + "//Redline.LayerDefinition");
                string featureClass = "Default:Redline";
                string geometry = "Geometry";
                string xml = string.Format(Properties.Resources.DebugLayer, fsId.ToString(), featureClass, geometry);

                var schema = new MgFeatureSchema("Default", "Redline schema");
                var cls = new MgClassDefinition();
                cls.Name = "Redline";

                var id = new MgDataPropertyDefinition("ID");
                id.DataType = MgPropertyType.Int32;
                id.SetAutoGeneration(true);

                var geom = new MgGeometricPropertyDefinition(geometry);
                geom.SpatialContextAssociation = "Default";
                geom.GeometryTypes = MgFeatureGeometricType.Curve | MgFeatureGeometricType.Point | MgFeatureGeometricType.Solid | MgFeatureGeometricType.Surface;

                var clsProps = cls.GetProperties();
                clsProps.Add(id);
                clsProps.Add(geom);
                
                var idProps = cls.GetIdentityProperties();
                idProps.Add(id);

                cls.DefaultGeometryPropertyName = geometry;
                var classes = schema.GetClasses();
                classes.Add(cls);

                //Create the feature source
                var create = new MgCreateSdfParams("Default", _map.GetMapSRS(), schema);
                featSvc.CreateFeatureSource(fsId, create);

                //Then the layer definition
                var bytes = Encoding.UTF8.GetBytes(xml);
                MgByteSource source = new MgByteSource(bytes, bytes.Length);
                resSvc.SetResource(ldfId, source.GetReader(), null);

                //Now create the runtime layer and add to map
                _redlineLayer = new MgdLayer(ldfId, resSvc);
                _redlineLayer.LegendLabel = "Redlining";
                _redlineLayer.Name = "Redline";
                _redlineLayer.Visible = true;
                _redlineLayer.Selectable = true;
                _redlineLayer.DisplayInLegend = true;

                var layers = _map.GetLayers();
                layers.Insert(0, _redlineLayer);

                //System.Diagnostics.Trace.TraceInformation("Created redline layer");
            }
        }

        static string MakeWktPolygon(double x1, double y1, double x2, double y2)
        {
            return "POLYGON((" + x1 + " " + y1 + ", " + x2 + " " + y1 + ", " + x2 + " " + y2 + ", " + x1 + " " + y2 + ", " + x1 + " " + y1 + "))";
        }

        static string MakeWktCircle(double x, double y, double r)
        {
            return "CURVEPOLYGON ((" + (x - r) + " " + y + " (CIRCULARARCSEGMENT (" + x + " " + (y - r) + ", " + (x + r) + " " + y + "), CIRCULARARCSEGMENT (" + x + " " + (y + r) + ", " + (x - r) + " " + y + "))))";
        }

        private void btnDigitizePoint_Click(object sender, EventArgs e)
        {
            viewer.DigitizePoint(OnPointDigitized);
        }

        private void OnPointDigitized(double x, double y)
        {
            CheckRedlineLayer();
            MgGeometry point = _geomFact.CreatePoint(_geomFact.CreateCoordinateXY(x, y));
            InsertRedlineGeometry(point);
        }

        private void btnDigitizeLine_Click(object sender, EventArgs e)
        {
            viewer.DigitizeLine(OnLineDigitized);
        }

        private void OnLineDigitized(double x1, double y1, double x2, double y2)
        {
            CheckRedlineLayer();
            MgCoordinateCollection coords = new MgCoordinateCollection();
            coords.Add(_geomFact.CreateCoordinateXY(x1, y1));
            coords.Add(_geomFact.CreateCoordinateXY(x2, y2));
            MgGeometry line = _geomFact.CreateLineString(coords);
            InsertRedlineGeometry(line);
        }

        private void btnDigitizePolyline_Click(object sender, EventArgs e)
        {
            viewer.DigitizeLineString(OnLineStringDigitized);
        }

        private void OnLineStringDigitized(double[,] coordinates)
        {
            CheckRedlineLayer();
            MgCoordinateCollection coords = new MgCoordinateCollection();
            for (int i = 0; i < coordinates.GetLength(0); i++)
            {
                coords.Add(_geomFact.CreateCoordinateXY(coordinates[i, 0], coordinates[i, 1]));
            }
            MgGeometry line = _geomFact.CreateLineString(coords);
            InsertRedlineGeometry(line);
        }

        private void btnDigitizeCircle_Click(object sender, EventArgs e)
        {
            viewer.DigitizeCircle(OnCircleDigitized);
        }

        private void OnCircleDigitized(double x, double y, double radius)
        {
            CheckRedlineLayer();
            MgGeometry geom = _wktRW.Read(MakeWktCircle(x, y, radius));
            InsertRedlineGeometry(geom);
        }

        private void btnDigitizeRectangle_Click(object sender, EventArgs e)
        {
            viewer.DigitizeRectangle(OnRectangleDigitized);
        }

        private void OnRectangleDigitized(double llx, double lly, double urx, double ury)
        {
            CheckRedlineLayer();
            MgGeometry geom = _wktRW.Read(MakeWktPolygon(llx, lly, urx, ury));
            InsertRedlineGeometry(geom);
        }

        private void btnDigitizePolygon_Click(object sender, EventArgs e)
        {
            viewer.DigitizePolygon(OnPolygonDigitized);
        }

        private void OnPolygonDigitized(double[,] coordinates)
        {
            CheckRedlineLayer();
            MgCoordinateCollection coords = new MgCoordinateCollection();
            for (int i = 0; i < coordinates.GetLength(0); i++)
            {
                coords.Add(_geomFact.CreateCoordinateXY(coordinates[i, 0], coordinates[i, 1]));
            }
            coords.Add(_geomFact.CreateCoordinateXY(coordinates[0, 0], coordinates[0, 1]));
            MgLinearRing ring = _geomFact.CreateLinearRing(coords);
            MgGeometry poly = _geomFact.CreatePolygon(ring, null);
            InsertRedlineGeometry(poly);
        }

        private void InsertRedlineGeometry(MgGeometry geom)
        {
            MgPropertyCollection feature = new MgPropertyCollection();
            MgByteReader agf = _agfRW.Write(geom);
            MgGeometryProperty geomProp = new MgGeometryProperty("Geometry", agf);
            feature.Add(geomProp);

            _redlineLayer.ForceRefresh();
            var reader = _redlineLayer.InsertFeatures(feature);
            int inserted = 0;
            while (reader.ReadNext())
            {
                inserted++;
            }
            reader.Close();
            if (inserted > 0)
                viewer.RefreshMap();
        }

        private void viewer_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "IsBusy")
            {
                var busy = viewer.IsBusy;
                btnDigitizeCircle.Enabled
                                       = btnDigitizeLine.Enabled
                                       = btnDigitizePoint.Enabled
                                       = btnDigitizePolygon.Enabled
                                       = btnDigitizePolyline.Enabled
                                       = btnDigitizeRectangle.Enabled  = !busy;
            }
            else if (e.PropertyName == "ActiveTool" || e.PropertyName == "DigitizingType")
            {
                UpdateButtonCheckedState();
            }
        }

        private void UpdateButtonCheckedState()
        {
            var dt = viewer.DigitizingType;
            btnDigitizeCircle.Checked = (dt == MapDigitizationType.Circle);
            btnDigitizeLine.Checked = (dt == MapDigitizationType.Line);
            btnDigitizePoint.Checked = (dt == MapDigitizationType.Point);
            btnDigitizePolygon.Checked = (dt == MapDigitizationType.Polygon);
            btnDigitizePolyline.Checked = (dt == MapDigitizationType.LineString);
            btnDigitizeRectangle.Checked = (dt == MapDigitizationType.Rectangle);
        }

        private void btnPlotToDwf_Click(object sender, EventArgs e)
        {
            var diag = new ResourceIdDialog();
            if (diag.ShowDialog() == DialogResult.OK)
            {
                var layoutId = diag.ResourceID;
                using (var save = new SaveFileDialog())
                {
                    save.Filter = "DWF Files (*.dwf)|*.dwf";
                    if (save.ShowDialog() == DialogResult.OK)
                    {
                        var renderSvc = (MgRenderingService)viewer.GetProvider().CreateService(MgServiceType.RenderingService);
                        var map = (MgdMap)viewer.GetMap();
                        var dwfVer = new MgDwfVersion("6.01", "1.2");

                        var layout = new MgLayout(layoutId, "TestPlot", MgPageUnitsType.Inches);
                        var plotSpec = new MgPlotSpecification(8.5f, 11.0f, MgPageUnitsType.Inches, 0.5f, 0.5f, 0.5f, 0.5f);

                        var result = renderSvc.GeneratePlot(map, plotSpec, layout, dwfVer);
                        var sink = new MgByteSink(result);
                        sink.ToFile(save.FileName);

                        MessageBox.Show("Saved to " + save.FileName);
                    }
                }
            }
        }

        private void loadCompactMapViewerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var map = viewer.GetMap();
            var resId = map.GetMapDefinition();

            var cv = new CompactViewer();
            cv.LoadMap(resId);
            cv.Show();
        }

        private void thisIsAGroupContextMenuToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var group = legend.GetSelectedGroup();
            if (group != null)
                MessageBox.Show("Selected group is: " + group.Name);
        }

        private void thisIsALayerContextMenuToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var layer = legend.GetSelectedLayer();
            if (layer != null)
                MessageBox.Show("Selected layer is: " + layer.Name);
        }

        private void showConnectionPoolStatusToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var featSvc = (MgdFeatureService)fact.CreateService(MgServiceType.FeatureService);
            var resp = featSvc.QueryCacheInfo();

            new XmlResponseDialog(resp).ShowDialog();
        }

        private void purgePooledConnectionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            using (var diag = new ResourceIdDialog())
            {
                if (diag.ShowDialog() == DialogResult.OK)
                {
                    var featSvc = (MgdFeatureService)fact.CreateService(MgServiceType.FeatureService);
                    featSvc.PurgeCache(diag.ResourceID);
                    MessageBox.Show("Pooled connections purged for: " + diag.ResourceID.ToString());
                }
            }
        }
    }
}
